﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Legend.Guarding;

namespace Legend
{
    public static class GuardingExtensionMethods
    {
        /// <summary>
        /// Throws an ArgumentNullException if the argument held in the
        /// specified ArgumentInfo(T) is null.
        /// </summary>
        /// <typeparam name="T">The type of the validated argument.</typeparam>
        /// <param name="argument">The argument info to validate.</param>
        /// <returns>The ArgumentInfo(T) instance.</returns>
        public static IArgumentInfoAccessor<T> IsNotNull<T>(this IArgumentInfo<T> argument)
        {
            if (argument == null)
            {
                throw new ArgumentNullException("argument", "The ArgumentInfo for the IsNotNull-method was null.");
            }

            if ((object)argument.Value == null)
            {
                throw CreateArgumentNullException(argument);
            }

            return argument;
        }

        /// <summary>
        /// Throws an ArgumentNullException if the nullable argument held in the
        /// specified ArgumentInfo(Nullable(T)) is null.
        /// </summary>
        /// <typeparam name="T">The type of the validated argument.</typeparam>
        /// <param name="argument">The argument info to validate.</param>
        /// <returns>An ArgumentInfo(T) instance.</returns>
        public static IArgumentInfoAccessor<T> IsNotNull<T>(this IArgumentInfo<T?> argument) where T : struct
        {
            if (argument == null)
            {
                throw new ArgumentNullException("argument", "The ArgumentInfo for the IsNotNull-method was null.");
            }

            if (!argument.Value.HasValue)
            {
                throw CreateArgumentNullException(argument);
            }

            return new ArgumentInfo<T>(argument.Value.Value).Named(argument.ArgumentName);
        }

        /// <summary>
        /// Throws an exception if the argument held in the ArgumentInfo(T) is null
        /// or an empty string.
        /// </summary>
        /// <param name="argument">The argument info to be validated.</param>
        /// <exception cref="ArgumentNullException">The argument is null or the argument is an empty string or
        /// the ArgumentInfo-is itself null.</exception>
        /// <returns>An IArgumentInfoAccessor(string) instance.</returns>
        public static IArgumentInfoAccessor<string> IsNotNullOrEmpty(this IArgumentInfo<string> argument)
        {
            Require.ThatArgument(argument).Named("argument").IsNotNull();

            if (string.IsNullOrEmpty(argument.Value))
            {
                throw CreateArgumentNullException(argument);
            }

            return argument;
        }

        private static ArgumentNullException CreateArgumentNullException<T>(IArgumentInfo<T> argument)
        {
            if (argument.ArgumentName != null)
            {
                return new ArgumentNullException(argument.ArgumentName);
            }
            else
            {
                return new ArgumentNullException();
            }
        }

        /// <summary>
        /// Validates that the argument is within the specified range or
        /// throws an ArgumentOutOfRangeException.
        /// </summary>
        /// <typeparam name="T">The type of argument to validate.</typeparam>
        /// <param name="argument">The argument to validate.</param>
        /// <param name="lowerBound">The lower bound of the valid range, a value
        /// equal to this value is considered to be in the range.</param>
        /// <param name="upperBound">The upper bound of the valid range, a value
        /// equal to this value is considered to be in the range.</param>
        /// <returns>An object that </returns>
        public static IArgumentInfoAccessor<T> IsInRange<T>(this IArgumentInfo<T> argument, T lowerBound, T upperBound) where T : IComparable<T>
        {
            Require.ThatArgument(argument).Named("argument").IsNotNull();

            if (lowerBound.CompareTo(argument.Value) > 0 || upperBound.CompareTo(argument.Value) < 0)
            {
                if (argument.ArgumentName != null)
                {
                    throw new ArgumentOutOfRangeException(argument.ArgumentName);
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }

            return argument;
        }
    }
}
